// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.ObjectModel;

namespace System.Diagnostics.Tracing
{
    /// <summary>
    /// NativeRuntimeEventSource is an EventSource that represents the ETW/EventPipe events emitted by the native runtime.
    /// Most of NativeRuntimeEventSource is auto-generated by scripts/genRuntimeEventSources.py based on the contents of the
    /// Microsoft-Windows-DotNETRuntime provider and will throw a NotImplementedException without hand-written overloads of the Events.
    /// To have a runtime event be fired from the managed code, you need to add a managed definition for that event
    /// and call into the native runtime that invoke the appropriate native sinks for the platform (i.e. ETW, EventPipe, LTTng).
    /// To see some examples of this, refer to NativeRuntimeEventSource.PortableThreadPool.NativeSinks.cs and NativeRuntimeEventSource.PortableThreadPool.cs.
    /// Then, modify genRuntimeEventSources.py to skip over the event so that it doesn't generate the dummy method.
    /// </summary>
    [EventSource(Guid = "E13C0D23-CCBC-4E12-931B-D9CC2EEE27E4", Name = EventSourceName)]
    [EventSourceAutoGenerate]
    internal sealed partial class NativeRuntimeEventSource : EventSource
    {
        internal const string EventSourceName = "Microsoft-Windows-DotNETRuntime";
        public static readonly NativeRuntimeEventSource Log = new NativeRuntimeEventSource();

        // This value does not seem to be used, leaving it as zero for now. It may be useful for a scenario that may involve
        // multiple instances of the runtime within the same process, but then it seems unlikely that both instances' thread
        // pools would be in moderate use.
        private const ushort DefaultClrInstanceId = 0;

        // Parameterized constructor to block initialization and ensure the EventSourceGenerator is creating the default constructor
        // as you can't make a constructor partial.
        private NativeRuntimeEventSource(int _) { }

#if FEATURE_PERFTRACING
        /// <summary>
        /// Dispatch a single event with the specified event ID and payload.
        /// </summary>
        /// <param name="eventID">The eventID corresponding to the event as defined in the auto-generated portion of the NativeRuntimeEventSource class.</param>
        /// <param name="osThreadID">The thread ID of the operating system thread.</param>
        /// <param name="timeStamp">The current timestamp.</param>
        /// <param name="activityId">The ID of the current activity.</param>
        /// <param name="childActivityId">The ID of the current child activity.</param>
        /// <param name="payload">A span pointing to the data payload for the event.</param>
        [NonEvent]
        internal unsafe void ProcessEvent(uint eventID, uint osThreadID, DateTime timeStamp, Guid activityId, Guid childActivityId, ReadOnlySpan<byte> payload)
        {
            // A simple fix to avoid dependencies brought by this method if event source is disabled via a feature switch.
            // Should be reconsidered when https://github.com/dotnet/runtime/issues/43657 is done.
            if (!IsSupported)
            {
                return;
            }

            // Make sure the eventID is valid.
            if (eventID >= m_eventData!.Length)
            {
                return;
            }

            // Decode the payload.
            object[] decodedPayloadFields = EventPipePayloadDecoder.DecodePayload(ref m_eventData[eventID], payload);

            var eventCallbackArgs = new EventWrittenEventArgs(this, (int)eventID, &activityId, &childActivityId)
            {
                OSThreadId = (int)osThreadID,
                TimeStamp = timeStamp,
                Payload = new ReadOnlyCollection<object?>(decodedPayloadFields)
            };

            DispatchToAllListeners(eventCallbackArgs);
        }
#endif // FEATURE_PERFTRACING
    }
}
